﻿using System;
using System.Diagnostics.Contracts;
using IWMAG.Core.EnhancedProperties.Extensions;
using IWMAG.Core.EnhancedProperties.PropertyNodes;

namespace IWMAG.Core.EnhancedProperties.ResourceProperties
{
    public static class ResourcePropertiesExtensions
    {
        #region Common
        private static void VerifyArgumentsNotNull<T>(IPropertyContainer owner, PropertyBacker<T> backer, IResource<T> staticResource)
        {
            Contract.Requires(owner != null);
            Contract.Requires(backer != null);
            Contract.Requires(staticResource != null);
        }

        private static void ModifyOrReplaceNodeAndNotifyPropertyManager<T>(IPropertyContainer owner, PropertyBacker<T> backer, IResource<T> resource, int priority, PropertyNodeList<T> actual)
        {
            actual.AddOrModifyListMember(priority, (i) => new ResourceIndirectNode<T>(i, resource), (node) =>
            {
                node.UpdateResource(resource);
                return true;
            });

            PropertyManager.RegisterPropertyAffected(owner.UniqueID, backer.UniqueID);
        }
        #endregion

        #region Static Resources
        const int DEFAULT_STATICRESOURCE_PRIORITY = 20;

        private static void VerifyStaticResourceEnabled<T>(PropertyBacker<T> backer)
        {
            if (!CheckStaticResourceEnabled(backer))
            {
                throw new NotSupportedException("Property cannot have a static resource value.");
            }
        }

        private static bool CheckStaticResourceEnabled<T>(PropertyBacker<T> backer)
        {
            return backer.Metadata.CanSetStaticResourceValue;
        }

        public static void SetStaticResourceValue<T>(this IPropertyContainer owner, PropertyBacker<T> backer, StaticResource<T> staticResource, int priority = DEFAULT_STATICRESOURCE_PRIORITY)
        {
            VerifyArgumentsNotNull<T>(owner, backer, staticResource);

            VerifyStaticResourceEnabled<T>(backer);

            var actual = backer.GetOrCreatePropertyNodeList(owner);

            ModifyOrReplaceNodeAndNotifyPropertyManager<T>(owner, backer, staticResource, priority, actual);
        }

        public static bool SetStaticResourceValue<T>(this TryPropertyBacker owner, PropertyBacker<T> backer, StaticResource<T> staticResource, int priority = DEFAULT_STATICRESOURCE_PRIORITY)
        {
            VerifyArgumentsNotNull<T>(owner.Container, backer, staticResource);

            var c = CheckStaticResourceEnabled<T>(backer);
            if (!c)
            {
                return false;
            }

            var actual = backer.GetOrCreatePropertyNodeList(owner.Container);

            ModifyOrReplaceNodeAndNotifyPropertyManager<T>(owner.Container, backer, staticResource, priority, actual);

            return true;
        }

       

        public static int AddStaticResourceValue<T>(this IPropertyContainer owner, PropertyBacker<T> backer, StaticResource<T> staticResource)
        {
            VerifyArgumentsNotNull<T>(owner, backer, staticResource);

            VerifyStaticResourceEnabled<T>(backer);

            var actual = backer.GetOrCreatePropertyNodeList(owner);
            var highest = actual.GetHighestPriority() + 1;
            ModifyOrReplaceNodeAndNotifyPropertyManager<T>(owner, backer, staticResource, highest, actual);

            return highest;
        }

        public static bool AddStaticResourceValue<T>(this TryPropertyBacker owner, PropertyBacker<T> backer, StaticResource<T> staticResource, ref int priority)
        {
            VerifyArgumentsNotNull<T>(owner.Container, backer, staticResource);

            var c = CheckStaticResourceEnabled<T>(backer);

            if (!c)
            {
                return false;
            }


            var actual = backer.GetOrCreatePropertyNodeList(owner.Container);
            var highest = actual.GetHighestPriority() + 1;
            ModifyOrReplaceNodeAndNotifyPropertyManager<T>(owner.Container, backer, staticResource, highest, actual);

            priority = highest;
            return true;
        }


        #endregion

        #region Dynamic Resources
        const int DEFAULT_DYNAMICRESOURCE_PRIORITY = 30;
        private static void VerifyDynamicResourceEnabled<T>(PropertyBacker<T> backer)
        {
            if (!CheckDynamicResourceEnabled(backer))
            {
                throw new NotSupportedException("Property cannot have a dynamic resource value.");
            }
        }

        private static bool CheckDynamicResourceEnabled<T>(PropertyBacker<T> backer)
        {
            return backer.Metadata.CanSetStaticResourceValue;
           
        }
        public static void SetDynamicResourceValue<T>(this IPropertyContainer owner, PropertyBacker<T> backer, DynamicResource<T> dynamicResource, int priority = DEFAULT_DYNAMICRESOURCE_PRIORITY)
        {
            VerifyArgumentsNotNull<T>(owner, backer, dynamicResource);

            VerifyDynamicResourceEnabled<T>(backer);


            var actual = backer.GetOrCreatePropertyNodeList(owner);

            ModifyOrReplaceNodeAndNotifyPropertyManager<T>(owner, backer, dynamicResource, priority, actual);
        }

        public static bool SetDynamicResourceValue<T>(this TryPropertyBacker owner, PropertyBacker<T> backer, DynamicResource<T> dynamicResource, int priority = DEFAULT_DYNAMICRESOURCE_PRIORITY)
        {
            VerifyArgumentsNotNull<T>(owner.Container, backer, dynamicResource);

            var c = CheckDynamicResourceEnabled<T>(backer);

            if (!c)
            {
                return false;
            }


            var actual = backer.GetOrCreatePropertyNodeList(owner.Container);

            ModifyOrReplaceNodeAndNotifyPropertyManager<T>(owner.Container, backer, dynamicResource, priority, actual);

            return true;
        }

        public static int AddDynamicResourceValue<T>(this IPropertyContainer owner, PropertyBacker<T> backer, DynamicResource<T> dynamicResource)
        {
            VerifyArgumentsNotNull<T>(owner, backer, dynamicResource);

            VerifyDynamicResourceEnabled<T>(backer);


            var actual = backer.GetOrCreatePropertyNodeList(owner);
            var highest = actual.GetHighestPriority() + 1;
            ModifyOrReplaceNodeAndNotifyPropertyManager<T>(owner, backer, dynamicResource, highest, actual);

            return highest;
        }

        public static bool AddDynamicResourceValue<T>(this TryPropertyBacker owner, PropertyBacker<T> backer, DynamicResource<T> dynamicResource, ref int priority)
        {
            VerifyArgumentsNotNull<T>(owner.Container, backer, dynamicResource);

            var c = CheckDynamicResourceEnabled<T>(backer);

            if (!c)
            {
                return false;
            }

            var actual = backer.GetOrCreatePropertyNodeList(owner.Container);
            var highest = actual.GetHighestPriority() + 1;
            ModifyOrReplaceNodeAndNotifyPropertyManager<T>(owner.Container, backer, dynamicResource, highest, actual);

            priority =  highest;

            return true;
        }
        #endregion
    }
}
